const util = require('util');
const debug = util.debuglog('oil-game:box:action');
const BigNumber = require("bignumber.js");
const { formatCoins } = require("../../upgrade/lib/action");

const {
  clearCountDown,   // 停止倒计时
  ownToWaitOpenBox,   // 将一个已拥有宝箱放置在待收取宝箱中
  startCountDown,   // 开始倒计时
  getOwnBoxInfo,    // 获取已拥有宝箱
  addOneOwnBox,   // 增加一个已拥有宝箱
  getWaitOpenBoxInfo,   // 获取待领取宝箱
  getwaitBoxNum,   //从数据库获取待领取宝箱数量
  getCountDownBoxInfo,    // 获取用户当前正在倒计时的宝箱信息
  delManyWaitOpenBox,    // 删除特定数量待领取宝箱
  getTempPrize,   // 获取用户暂存的奖品
  saveTempPrize,    // 打开宝箱，将奖品暂存
  decodeBoxOverTimeKey,   // 解码宝箱redis超时字段
  addBoxGiveLog,    //添加宝箱获取记录
  addBoxOpenLog,    //添加宝箱打开记录
  getTodayOilNum,   // 获取当日获取石油数量
} = require('./cache');
const {
  Msg,
  boxMaxSize,   // 待收取宝箱最大数量
  PushRoute,    // 客户端需要监听的接口
  countDownNum,   //倒计时
} = require('./consts');
const {
  addCoins,   //增加金币
  addOil,   //增加石油
  getUpgradeDate,   //获取升级相关数据
  getUserData,    //获取用户相关信息
} = require('./api');
const {
  getOwnBoxInfo: getOwnBoxInfoByDao
} = require('./dao');

// 开始倒计时
exports.startCountDown = async function(uid, sid){
  //检测已领取宝箱是否达上限
  if(await isWaitBoxMaxNum.call(this, uid)){
    debug("uid = %s 开始倒计时，已领取宝箱达到上限，即将推出", uid);
    return;
  }
  //检测是否有正在倒计时的宝箱
  if(await getCountDownBoxInfo.call(this, uid)){
    debug("uid = %s 开始倒计时，检测存在正在倒计时的宝箱，即将推出", uid);
    return;
  }
  //获取当前已拥有宝箱
  let ownBox = await getOwnBoxInfo.call(this, uid);
  //从数据库获取已拥有宝箱
  if(!ownBox) ownBox = await getOwnBoxInfoByDao.call(this, uid);
  //检测已拥有宝箱是否为空
  if(ownBox.length < 1){
    debug("uid = %s 开始倒计时，检测已拥有宝箱为空，即将推出", uid);
    return;
  }

  //需要倒计时的宝箱ID
  let boxId = ownBox[0].id;
  //开始倒计时
  await startCountDown.call(this, uid, boxId, sid);

  //通知客户端开启倒计时事件
  this.app.get('channelService').pushMessageByUids(
    PushRoute.CHEST_START_COUNT_DOWN, { id: boxId }, [{ uid, sid }]);

  return boxId;
}

// 清空倒计时
exports.clearCountDown = async function (uid, sid, boxId){
  debug("uid = %s  sid = %s boxId = %s  清空倒计时", uid, sid, boxId);
  //未找到宝箱ID
  if(!boxId) throw new Error(Msg.NO_SEARCH_BOX_ID);

  //检测已领取宝箱是否到达上限
  if(await isWaitBoxMaxNum.call(this, uid)){
    debug("uid = %s  清空倒计时异常，宝箱数量达到上限", uid);
    throw new Error(Msg.WAIT_BOX_MAX_NUM);
  }
  //停止倒计时
  await clearCountDown.call(this, uid, boxId, sid);

  //从已拥有宝箱中获取一个放置在待收取宝箱中
  await ownToWaitOpenBox.call(this, uid, boxId);

  let num = await getwaitBoxNum.call(this, uid);
  //倒计时结束事件发送给客户端
  this.app.get('channelService').pushMessageByUids(
    PushRoute.CHEST_COUNT_DOWN_END, {num}, [{ uid, sid }]);

  // 开始下一次倒计时
  await exports.startCountDown.call(this, uid, sid);
}

// 宝箱倒计时自然结束
exports.endCountDown = async function (key){
  //从key中获取相应字段
  let { uid, boxId, sid } = decodeBoxOverTimeKey(key);
  // 清空倒计时
  await exports.clearCountDown.call(this, uid, sid, boxId);
}

// 系统随机发放宝箱到已拥有宝箱
exports.addOneOwnBox = async function (uid, sid, boxId){
  // 增加一个已拥有宝箱
  await addOneOwnBox.call(this, uid, boxId);

  // 开始倒计时
  await exports.startCountDown.call(this, uid, sid);

  //记录当前宝箱获取记录到数据库
  await addBoxGiveLog.call(this, uid, { box_id: boxId, give_at: new Date()});

  //宝箱增加消息，给客户端发送当前增加的宝箱ID
  this.app.get('channelService').pushMessageByUids(
    PushRoute.CHEST_INCREASE, {}, [{ uid, sid }]);
}

// 获取待领取宝箱数量
exports.getwaitBoxNum = async function (uid){
  let waitOpenBoxNum = await getwaitBoxNum.call(this, uid);
  return waitOpenBoxNum;
}

// 获取已拥有宝箱，待收取宝箱，当前倒计时宝箱ID和倒计时开启时间
exports.getIndexBoxInfo = async function (uid, frontendId){
  debug("uid = %s 获取已拥有宝箱，待收取宝箱，当前倒计时宝箱ID和倒计时开启时间", uid)
  // 获取已拥有宝箱
  let ownBoxInfo = await getOwnBoxInfo.call(this, uid);
  // 获取待领取宝箱
  let waitOpenBoxInfo = await getWaitOpenBoxInfo.call(this, uid);
  //开始倒计时 
  await exports.startCountDown.call(this, uid, frontendId);

  // 获取用户当前正在倒计时的宝箱信息
  // nowCountDownId  宝箱ID 
  // nowBoxCreated, 宝箱开始倒计时时间
  let nowCountDownBox = await getCountDownBoxInfo.call(this, uid);
  //将宝箱倒计时修改为秒数
  if(nowCountDownBox && nowCountDownBox.nowBoxCreated){
    nowCountDownBox.countDownNum = new Date(Number(nowCountDownBox.nowBoxCreated) + Number(countDownNum)) - new Date();
    nowCountDownBox.countDownNum = parseInt(Number(nowCountDownBox.countDownNum.valueOf())/1000);
  }

  return {ownBoxLen: ownBoxInfo.length , waitOpenBoxLen: waitOpenBoxInfo.length, nowCountDownBox};
}

// 打开宝箱，将奖品暂存
exports.openBox = async function (session, uid, sid) {
  debug("uid = %s  sid = %s 打开宝箱，将奖品暂存", uid, sid)
  //检测待领取宝箱是否为空
  let waitOpenBox = await getWaitOpenBoxInfo.call(this, uid);
  if(waitOpenBox.length < 1){
    debug("uid = %s  sid = %s 打开宝箱，待领取宝箱为空", uid, sid)
    throw new Error(Msg.NO_HAVE_WAIT_BOX);
  }

  // 获取奖品
  let prizeAry = await randomOpenBoxPrize.call(this, session, waitOpenBox.length);
  debug("uid = %s  打开宝箱，待领取奖品 = ", uid, JSON.stringify(prizeAry));

  // 打开宝箱，将奖品暂存，宝箱获取的金币数为普通Number，方便后期调用
  await saveTempPrize.call(this, uid, prizeAry);
  //所有的金币奖励
  let allPrizeCoins = new BigNumber(0);
  //所有的石油奖励
  let allPrizeOil = 0;
  // 返回奖品和打开的宝箱数量，宝箱获取的金币数为格式化后的，方便游戏端查看
  prizeAry = prizeAry.map(item => {
    if(item.type == "coins"){
      let itemBigNum = new BigNumber(item.num);
      allPrizeCoins = allPrizeCoins.plus(itemBigNum);
      item.num = formatCoins(itemBigNum);
    }else if(item.type == "oil"){
      allPrizeOil += item.num;
    }
    return item;
  });
  //金币奖励
  allPrizeCoinsDouble = allPrizeCoins.times(2);
  //单倍
  allPrizeCoins = formatCoins(allPrizeCoins);
  //双倍
  allPrizeCoinsDouble = formatCoins(allPrizeCoinsDouble);

  //添加宝箱打开记录
  addBoxOpenLog.call(this, uid, prizeAry.map((item,index) => ({
    box_id: waitOpenBox[index].id,   //宝箱ID
    grade: item.grade,    //量级，少量，大量，巨量
    type: item.type,    //奖品类型，金币或石油
    num: item.num,    //奖品数量
    open_at: new Date(),    //打开时间
  })));

  return {prizeAry, allPrizeCoins, allPrizeCoinsDouble, allPrizeOil, allPrizeOilDouble: allPrizeOil*2};
}

// 领取奖品，multiple为倍数
exports.obtainPrize = async function (session, multiple = 1) {
  let { uid, frontendId } = session;
  debug("uid = %s  sid = %s 领取奖品", uid, frontendId)

  //根据奖品更新数据，返回待领取宝箱数量
  let openBoxLen = await updatePrizeResult.call(this, session, uid, multiple);
  
  //删除待领取宝箱
  debug("uid = %s  sid = %s 领取奖品，待领取宝箱数量 = %s", uid, frontendId, openBoxLen);
  await delManyWaitOpenBox.call(this, uid, openBoxLen);

  //开始倒计时 
  await exports.startCountDown.call(this, uid, frontendId);
}

// 验证待领取宝箱数量是否达到上限
async function isWaitBoxMaxNum(uid){
  let waitOpenBoxNum = await getwaitBoxNum.call(this, uid);
  return waitOpenBoxNum >= boxMaxSize;
}

//根据奖品更新数据
async function updatePrizeResult(session, uid, multiple){
  let tempPrize = await getTempPrize.call(this, uid);

  // 获取用户暂存的奖品
  for(let i = 0; i < tempPrize.length; i++){
    let item = tempPrize[i];
    //更新用户数据
    let changeNum = multiple * Number.parseInt(item.num);
    switch(item.type){
      case "coins":
        debug("uid = %s 领取奖品，更新金币 = ", uid, changeNum);
        await addCoins.call(this, session, changeNum);
        break;
      case "oil":
        debug("uid = %s 领取奖品，更新石油 = ", uid, changeNum);
        await addOil.call(this, session, changeNum);
        break;
    }
  }
  //返回被打开的待领取宝箱数量
  return tempPrize.length;
}

//随机开始宝箱奖品
async function randomOpenBoxPrize(session, waitOpenBoxLen){
  //获取采集收入速度
  let {incomeRateNum} = await getUpgradeDate.call(this, session);
  //获取单日石油最高上限
  let maxOilFormula = await getMaxOilFormula.call(this, session);
  //获取本日石油数量
  let todayOilNum = await getTodayOilNum.call(this, session.uid)

  let prizeAry = [];
  for(let i = 0; i < waitOpenBoxLen; i++){
    //生成0-100的随机数 
    let randomNum = parseInt(Math.random()*(100+1),10);
    //获取石油概率为15%，并且当日获取石油没有到达上限
    if(randomNum > 85 && todayOilNum < maxOilFormula){   
      prizeAry.push(await randomGetOil.call(this, maxOilFormula));
    }else{    //获取金币
      prizeAry.push(await randomGetCoins.call(this, incomeRateNum));
    }
  }
  return prizeAry;
}

//随机获取金币
async function randomGetCoins(incomeRate){
  debug("incomeRate = %s  随机获取金币", incomeRate);
  let min = 5*100, max = 30*500;    //修改为原先的100到500倍
  let multipNum = parseInt(Math.random()*(max-min+1)+min,10);

  //等级，少量，大量，巨量
  let grade = "";
  if(multipNum < (min+(max-min)/0.3)) grade = "few";
  else if(multipNum < (min+(max-min)/0.6)) grade = "large";
  else grade = "huge";

  let res = { grade, type: "coins", num: Number(incomeRate) * multipNum };
  debug("随机获取金币成功  %s", JSON.stringify(res));

  return res;
}

//随机获取石油数量
async function randomGetOil(maxOilFormula){
  //每日石油获取最高的十分之一
  let getOilNum = parseInt(maxOilFormula/10);

  //石油获取大小
  if(getOilNum < 5) grade = "few";
  else if(getOilNum < 10) grade = "large";
  else grade = "huge";

  let res = { grade, type: "oil", num: getOilNum < 1 ? 1 : getOilNum };
  debug("随机获取石油成功  %s", JSON.stringify(res));

  return res;
}

//返回石油上限
async function getMaxOilFormula(session){
  //深度，品质，速度等级
  let {depth_level, quality_level, spreed_level} = await getUpgradeDate.call(this, session);
  //视频次数，徒弟数，徒孙数
  let {video_num, apprentice_num, disciple_num} = await getUserData.call(this, session);

  //团长等级
  let userGrade = 1;    
  if(disciple_num >= 3000 && apprentice_num >= 1000) userGrade = 6;
  else if(disciple_num >= 1500 && apprentice_num >= 500) userGrade = 5;
  else if(disciple_num >= 600 && apprentice_num >= 200) userGrade = 4;
  else if(disciple_num >= 150 && apprentice_num >= 50) userGrade = 3;
  else if(disciple_num >= 0 && apprentice_num >= 1) userGrade = 2;

  //每日石油获取最高公式
  let maxOilFormula = 0;
  if(video_num >= 30)    //看视频完成
    maxOilFormula = (depth_level/100 + quality_level/100 + spreed_level/100 + video_num/30 + apprentice_num/100 + userGrade/6) * 20;
  else    //看视频未完成
    maxOilFormula = (depth_level/100 + quality_level/100 + spreed_level/100 + video_num/30 + apprentice_num/100 + userGrade/6) * 10;

  debug("maxOilFormula = %s  获取单日石油上限", maxOilFormula); 
  return maxOilFormula;
}
